package cn.com.duiba.quanyi.open.demo.sm;

import cn.com.duiba.quanyi.open.demo.utils.Base62;
import com.alibaba.fastjson.JSONObject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Objects;

/**
 * 国密sm4加密工具
 *
 * @author zzy
 * @create 2023/11/28 19:00
 **/
public class SM4Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public static final String ALGORITHM_NAME = "SM4";
    //密钥长度128位
    private static final int DEFAULT_KEY_SIZE = 128;
    private static final String ENCODING = "UTF-8";
    // 加密算法 分组加密模式 分组填充方式
    // PKCS5Padding 以8个字节为一组进行分组加密
    private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";


    public static String generateKey() {
        return new String(Hex.toHexString(generateKey(DEFAULT_KEY_SIZE)));
    }

    private static byte[] generateKey(int keySize) {
        KeyGenerator kg;
        try {
            kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    // ECB模式（electronic code book）
    private static Cipher generateEcbCipher(String algorithm, int mode, byte[] key) {
        try {
            Cipher cipher = Cipher.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME);
            Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
            cipher.init(mode, sm4Key);
            return cipher;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

    private static byte[] encryptEcbPadding(byte[] key, byte[] data) {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        try {
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    private static byte[] decryptEcbPadding(byte[] key, byte[] cipherText) {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
        try {
            return cipher.doFinal(cipherText);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * sm4加密
     *
     * @param hexKey 16进制密钥
     * @param data   要加密的数据
     * @return
     */
    public static String encryptEcb(String hexKey, String data) {
        //16进制字符串
        byte[] keyBytes = ByteUtils.fromHexString(hexKey);
        byte[] dataBytes;
        try {
            dataBytes = data.getBytes(ENCODING);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        //加密后的数组
        byte[] cipherBytes = encryptEcbPadding(keyBytes, dataBytes);
        return Base62.getInstance().encode(cipherBytes);
    }

    /**
     * sm4解密
     *
     * @param hexKey       16进制密钥
     * @param cipherBase62 base62编码加密字符串
     * @return
     */
    public static String decryptEcb(String hexKey, String cipherBase62) {
        //16进制字符串
        byte[] keyBytes = ByteUtils.fromHexString(hexKey);
        byte[] cipherBytes = Base62.getInstance().decode(cipherBase62);
        //加密后的数组
        byte[] srcBytes = decryptEcbPadding(keyBytes, cipherBytes);
        // 用于接收解密后的字符串
        String data;
        try {
            data = new String(srcBytes, ENCODING);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return data;
    }

    public static void main(String[] args) {
        //密钥
        String key = "69c8782f14558429878f2f1a1922cf2f";
        System.out.println("密钥:" + key);

        JSONObject data = new JSONObject();
        data.put("param1", "abc");
        data.put("param2", 123456);
        data.put("param3", "https://www.duiba.cn");
        String dataSrc = data.toJSONString();

        System.out.println("加密前原文:" + dataSrc);
        //加密
        String bizData = encryptEcb(key, dataSrc);
        System.out.println("密文bizData：" + bizData);
        //解密
        String deString = decryptEcb(key, bizData);
        String deString2 = decryptEcb(key, "5E5tUQw90A9LCoIPQeKt804le3ypu0adGSHoLZZ6ljrR6mRBmQ7AqXPWm7vpsmzkEEqk2GRp4OhBw2nQx3PoJy4ubp2ycJlh0Wp7VCuvTic1");
        System.out.println("解密后原文deString:" + deString);
        System.out.println("解密后原文deString2:" + deString2);
        System.out.println("加解密是否成功:" + Objects.equals(dataSrc, deString));
    }

}
